package com.autumn.mybatis.factory;

import java.io.Closeable;
import java.io.IOException;
import java.lang.reflect.Method;
import java.sql.Connection;
import java.sql.SQLException;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;

import javax.annotation.PreDestroy;
import javax.sql.DataSource;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.beans.factory.DisposableBean;
import org.springframework.jdbc.datasource.AbstractDataSource;

import com.autumn.mybatis.provider.IDbProvider;
import com.autumn.util.reflect.ReflectUtils;

/**
 * 动态路油数据源抽象
 * 
 * @author 老码农
 *         <p>
 *         Description
 *         </p>
 * @date 2017-12-30 11:25:20
 */
public abstract class AbstractDynamicRoutingDataSource extends AbstractDataSource implements Closeable, DisposableBean {

	/**
	 * 日志
	 */
	private final Log logger = LogFactory.getLog(this.getClass());

	/**
	 * 获取当前数据源
	 * 
	 * @return
	 */
	protected abstract DataSource getCurrentDataSource();

	private Map<DataSource, Method> dataSourceSet = new ConcurrentHashMap<>();

	private final IDataSourceFactory dataSourceFactory;
	private final IDbProvider provider;

	/**
	 * 实例化
	 * 
	 * @param dataSourceFactory
	 * @param provider
	 */
	public AbstractDynamicRoutingDataSource(IDataSourceFactory dataSourceFactory, IDbProvider provider) {
		this.dataSourceFactory = dataSourceFactory;
		this.provider = provider;
		this.logger.info("注册 DataSource 路由:" + this);
	}

	/**
	 * 获取数据源工厂
	 * 
	 * @return
	 *
	 */
	public IDataSourceFactory getDataSourceFactory() {
		return dataSourceFactory;
	}

	/**
	 * 获取数据驱动提供者
	 * 
	 * @return
	 *
	 */
	public IDbProvider getProvider() {
		return provider;
	}

	/**
	 * 添加数据源到关闭流中，spring调用 destroy 时应用，会自动搜索 close 函数和 Closeable 接口
	 * 
	 * @param dataSource
	 *            数据源
	 */
	protected final void addDataSourceDestroy(DataSource dataSource) {
		if (dataSource == null) {
			return;
		}
		synchronized (this) {
			if (dataSource instanceof Closeable) {
				try {
					Method method = dataSource.getClass().getMethod("close", new Class<?>[0]);
					dataSourceSet.put(dataSource, method);
				} catch (Exception e) {

				}
			} else {
				Method method = ReflectUtils.findMethod(dataSource.getClass(), "close", void.class, new Class<?>[0]);
				if (method == null) {
					method = ReflectUtils.findMethod(dataSource.getClass(), "destroy", void.class, new Class<?>[0]);
				}
				if (method != null) {
					dataSourceSet.put(dataSource, method);
				}
			}
		}
	}

	@Override
	public final Connection getConnection() throws SQLException {
		DataSource ds = getCurrentDataSource();
		return ds.getConnection();
	}

	@Override
	public final Connection getConnection(String username, String password) throws SQLException {
		DataSource ds = getCurrentDataSource();
		return ds.getConnection(username, password);
	}

	@Override
	@SuppressWarnings("unchecked")
	public final <T> T unwrap(Class<T> iface) throws SQLException {
		if (iface.isInstance(this)) {
			return (T) this;
		}
		DataSource ds = getCurrentDataSource();
		return ds.unwrap(iface);
	}

	@Override
	public final boolean isWrapperFor(Class<?> iface) throws SQLException {
		DataSource ds = getCurrentDataSource();
		return (iface.isInstance(this) || ds.isWrapperFor(iface));
	}

	@PreDestroy
	@Override
	public void destroy() throws Exception {
		this.close();
	}

	/**
	 * 关闭
	 */
	@Override
	public final void close() throws IOException {
		synchronized (this) {
			try {
				for (Map.Entry<DataSource, Method> entry : dataSourceSet.entrySet()) {
					try {
						if (entry.getKey() instanceof Closeable) {
							Closeable closeable = (Closeable) entry.getKey();
							closeable.close();
						} else {
							entry.getValue().invoke(entry.getKey());
						}
					} catch (Exception e) {
						logger.error(e.getMessage(), e);
					}
				}
			} finally {
				dataSourceSet.clear();
			}
		}
	}

}
